package com.sc.socket.chatroom.client;

import com.sc.socket.chatroom.constants.UDPConstants;
import com.sc.socket.common.ByteUtils;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * @author shuc001
 * @Date 2019-02-05 11:55
 */
public class ClientSearcher {

    public static ServerInfo searchServer(int timeout) {
        System.out.println("UDP SEARCH START");
        CountDownLatch countDownLatch  = new CountDownLatch(1);
        Listener listener = null;
        try{
            listener = listen(countDownLatch);
            sendBroadCast();
            countDownLatch.await(timeout, TimeUnit.MILLISECONDS);
        }catch (Exception e){
            e.printStackTrace();
        }
        System.out.println("search finish");
        if(listener==null){
            return null;
        }
        List<ServerInfo> devices = listener.getAll();
        if(devices.size()>0) {
            return devices.get(0);
        }
        return null;
    }

    private static void sendBroadCast() throws IOException {
        System.out.println("send broad cast");
        DatagramSocket ds = new DatagramSocket();
        //构建会送数据
        ByteBuffer byteBuffer =ByteBuffer.allocate(128);
        byteBuffer.put(UDPConstants.HEADER);
        byteBuffer.putShort((short) 1);
        byteBuffer.putInt(UDPConstants.CLIENT_PORT_RESP);

        DatagramPacket response = new DatagramPacket(byteBuffer.array(), byteBuffer.position()+1);
        //广播
        response.setAddress(InetAddress.getByName("255.255.255.255"));
        response.setPort(UDPConstants.SERVER_PORT);
        ds.send(response);
        ds.close();
        System.out.println("send finish");
    }

    private static Listener listen(CountDownLatch countDownLatch) throws InterruptedException {
        System.out.println("begin listen");
        CountDownLatch countDown = new CountDownLatch(1);

        Listener listener = new Listener(UDPConstants.CLIENT_PORT_RESP,countDownLatch,countDown);
        listener.start();
        countDown.await();
        return listener;
    }

    private static class Listener extends Thread{

        private final int listenPort;
        private final CountDownLatch receiveDownLatch;
        private final CountDownLatch startDownLatch;
        private List<ServerInfo> devices = new ArrayList<>();
        private boolean done = false;
        private DatagramSocket ds = null;
        final byte[] buffer = new byte[128];
        int minLen = UDPConstants.HEADER.length+2+4;

        public Listener(int clientPortResp, CountDownLatch countDownLatch, CountDownLatch countDown) {
            this.listenPort = clientPortResp;
            this.receiveDownLatch = countDownLatch;
            this.startDownLatch = countDown;
        }

        @Override
        public void run() {
            startDownLatch.countDown();
            try{
                ds = new DatagramSocket(listenPort);
                DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
                while (!done){
                    ds.receive(packet);
                    String clientIp = packet.getAddress().getHostAddress();
                    int  port =  packet.getPort();
                    int length  = packet.getLength();

                    //头+short 2字节指令+ int 4字节端口
                    boolean isValid  = length>=minLen&& ByteUtils.startWith(UDPConstants.HEADER);
                    System.out.println("收到消息: ip"+clientIp+" port "+port+" isValid "+isValid);
                    if(!isValid){
                        continue;
                    }
                    ByteBuffer byteBuffer = ByteBuffer.wrap(buffer,UDPConstants.HEADER.length,length);
                    short cmd = byteBuffer.getShort();
                    int resPort = byteBuffer.getInt();
                    if(cmd!=2||resPort<=0){
                        continue;
                    }
                    String sn = new String(buffer,minLen,length-minLen);
                    ServerInfo info = new ServerInfo(clientIp,resPort,sn);
                    devices.add(info);
                    receiveDownLatch.countDown();
                }

            }catch (Exception e){

            }finally {
                close();
            }
            System.out.println("search finish");
        }

        private void close() {
            if(ds!=null){
                ds.close();
                ds=null;
            }
        }

        public List<ServerInfo> getAll() {
            done=true;
            close();
            return devices;
        }
    }
}
